package PingPong;

import java.awt.event.*;

import java.awt.*;

import javax.swing.JPanel;
import javax.swing.Timer;
import javax.vecmath.Vector2d;

import java.util.Date;
import java.net.*;
import java.io.*;

public class Client extends JPanel implements Commons, ActionListener {

	public Client( String ip ) throws Exception {
		 addKeyListener(new TAdapter());
		 setFocusable(true);
		 
		 myPaddle = new Paddle( Paddle.Player.Left );
		 opponentPaddle = new Paddle( Paddle.Player.Right );
		 
		 ball = new Ball();
		 
		 try {
			 server = new PingPongServer( ip );
		 } catch( Exception e ) {
			throw e;			 
		 }
		 
		 Thread tServer = new Thread(server);
		 tServer.start();
		 
		 time = new Date();
		 timer = new Timer(DELAY, this);
		 timer.start();
	}
	
	public void actionPerformed(ActionEvent arg0) {
		long passed = new Date().getTime() - time.getTime();
		time = new Date();
		
		myPaddle.move( passed );
		opponentPaddle.move( passed );
		
		if( gameStart ) {
			ball.move( passed );
			check();
		}
		
		repaint();
	}
	
	public void check() {//TODO przyspieszyc
		//todo ogarnac to bo do dupy, zrobione zeby dzialalo
		Vector2d ballVector = new Vector2d(Math.sin( Math.toRadians( ball.getAngle() ) ), Math.cos( Math.toRadians( ball.getAngle() ) ));
				
		if( ball.getY() < 0 || ball.getY() > Commons.BOARD_HEIGHT ) {
			Vector2d wall = new Vector2d( 0,1 );
			wall.normalize();
			wall.set( wall.getX() * ballVector.dot(wall) * 2, wall.getY() * wall.dot(ballVector) * 2 );
			ballVector.sub( wall );
			
			if( ball.getAngle() > 180 )
				ball.setAngle( 360 - Math.toDegrees( ballVector.angle(new Vector2d(0,1)) )  );
			else 
				ball.setAngle( Math.toDegrees( ballVector.angle(new Vector2d(0,1)) ) );
		}
		
		if( myTurn ) {//TODO
			Rectangle p = myPaddle.getRectangle();
			Rectangle b = ball.getRectangle();
			
			if( b.intersects(p) ) {
				Vector2d wall = new Vector2d( 1, 0 );
				wall.normalize();
				wall.set( wall.getX() * ballVector.dot(wall) * 2, wall.getY() * wall.dot(ballVector) * 2 );
				ballVector.sub( wall );
				
				if( ball.getAngle() < 180 )
					ball.setAngle( 360 - Math.toDegrees( ballVector.angle(new Vector2d(0,1)) )  );
				else 
					ball.setAngle( Math.toDegrees( ballVector.angle(new Vector2d(0,1)) ) );
	
				if( b.intersects(p) )
					ball.captureSpeed(myPaddle);
				
				myTurn = false;
				server.sendBallPos();
				server.sendMyPaddlePos();
				
			} else if( ball.getX() <= 0 || ball.getX() >= Commons.BOARD_WIDTH ) {
				if( myPaddle.getPlayer() == Paddle.Player.Left ) {
					rightScore++;
					myTurn = true;
				} else {
					leftScore++;
					myTurn = false;
				}
				server.sendLoose();
			}
		}
	}
	
	public void paint(Graphics g) {
		super.paint(g);
		
		if( gameEnd ) {
			g.drawString("Game over", BOARD_WIDTH / 2, BOARD_HEIGHT / 2);
		}
		
		g.setColor(Color.black);
		g.fillRect(0, 0, BOARD_WIDTH, BOARD_HEIGHT);
		g.setColor(Color.white);
		
		/*g.drawString(myPaddle.getSpeed()+" ", 20, 20);
		g.drawString(ball.getAngle()+" ", 20, 30);
		g.drawString(ball.getSpeed()+" ", 20, 40);
		*/
		g.drawString(leftScore+" : "+rightScore, BOARD_WIDTH / 2, 20);
		
		g.fillOval( ball.getX(), ball.getY(), BALL_SIZE, BALL_SIZE );
		
		if( myPaddle.getPlayer() == Paddle.Player.Left ) {
			g.fillRect(0, myPaddle.getY(), PADDLE_WIDTH, PADDLE_HEIGHT);
		} else {
			g.fillRect(BOARD_WIDTH - PADDLE_WIDTH, myPaddle.getY(), PADDLE_WIDTH, PADDLE_HEIGHT);
		}
		
		if( opponentPaddle.getPlayer() == Paddle.Player.Left ) {
			g.fillRect(0, opponentPaddle.getY(), PADDLE_WIDTH, PADDLE_HEIGHT);
		} else {
			g.fillRect(BOARD_WIDTH - PADDLE_WIDTH, opponentPaddle.getY(), PADDLE_WIDTH, PADDLE_HEIGHT);
		}
	}
	
	private class TAdapter extends KeyAdapter {

        public void keyPressed(KeyEvent e) {
        	int key = e.getKeyCode();

            if (key == KeyEvent.VK_UP) {
                myPaddle.up();
                server.send("up");
                server.sendMyPaddlePos();
            }

            if (key == KeyEvent.VK_DOWN) {
                myPaddle.down();
                server.send("down");
                server.sendMyPaddlePos();
            }
        }

        public void keyReleased(KeyEvent e) {
        	int key = e.getKeyCode();

            if (key == KeyEvent.VK_UP) {
                myPaddle.stop();
                server.send("stop");
                server.sendMyPaddlePos();
            }

            if (key == KeyEvent.VK_DOWN) {
                myPaddle.stop();
                server.send("stop");
                server.sendMyPaddlePos();
            }
        }
    }
	
	private class PingPongServer implements Runnable {
		public PingPongServer( String address ) throws Exception {
			try {
				System.out.println("Connecting...");
				Socket s = new Socket(address,PORT);
				 
				in = new BufferedReader(new InputStreamReader(s.getInputStream()));
				out = new PrintWriter(s.getOutputStream(), true);
				 
				if( in.readLine().equals("right") ) {
					myPaddle = new Paddle( Paddle.Player.Right );
					opponentPaddle = new Paddle( Paddle.Player.Left );
					myTurn = false;
				} else {				
					System.out.println("Connected. Waiting for opponent.");
				}
				
				if( in.readLine().equals("start") ) {
					gameStart = true;
					System.out.println("Game commercing.");
				}
			 } catch( IOException e ) {
				 throw new Exception("Error while connecting to the server.");
			 }
		}
		
		public void send( String msg ) {
			out.println( msg );
		}
		
		public void sendMyPaddlePos() {
			String msg = "paddle";
			send(msg);
			msg = myPaddle.getY()+":"+myPaddle.getSpeed();
			send(msg);
		}
		
		public void sendBallPos() {
			String msg = "ball";
			send(msg);
			msg = ball.getX() + ":" + ball.getY() + ":" + ball.getAngle() + ":" + ball.getSpeed() + ":" + ball.getTurn();
			send(msg);
		}
		
		public void sendLoose() {
			send("loose");
		}
		
		public void run() {
			try {
				while(true) {
					String msg = in.readLine();
					if( msg.equals("up") ) {
						opponentPaddle.up();
					} else if( msg.equals("down") ) {
						opponentPaddle.down();
					} else if( msg.equals("stop") ) {
						opponentPaddle.stop();
					} else if( msg.equals("ball") ) {
						String msg2 = in.readLine();
						String[] tab = msg2.split(":");
						ball.setX(Integer.parseInt(tab[0]));
						ball.setY(Integer.parseInt(tab[1]));
						ball.setAngle(Double.parseDouble((tab[2])));
						ball.setSpeed(Integer.parseInt(tab[3]));
						ball.setTurn(Boolean.parseBoolean(tab[4]));
						myTurn = true;
					} else if( msg.equals("paddle") ) {
						String msg2 = in.readLine();
						String[] tab = msg2.split(":");
						opponentPaddle.setY(Integer.parseInt(tab[0]));
						opponentPaddle.setSpeed(Integer.parseInt(tab[1]));
					} else if( msg.equals("loose") ) {
						if( myPaddle.getPlayer() == Paddle.Player.Left ) {
							leftScore++;
							myTurn = true;
						} else {
							rightScore++;
							myTurn = false;
						}
						ball.init();
						sendBallPos();
					} else if( msg.equals("end") ) {
						gameEnd = true;
						return;
					}
				}
			} catch( IOException e ) {
				gameEnd = true;
			}
		}
		
		private BufferedReader in;
		private PrintWriter out;
	}
	
	private Paddle myPaddle, opponentPaddle;
	private PingPongServer server;
	private boolean myTurn = true;
	private Ball ball;
	private Timer timer;
	private Date time;
	private boolean gameStart = false;
	private boolean gameEnd = false;
	private int leftScore = 0, rightScore = 0;
}
